#include "sync.h"
#include "interrupt.h"
#include "debug.h"

/* 初始化信号量 */
void sema_init(struct semaphore *psema, uint8_t value) {
    psema->value = value;
    list_init(&psema->waiters);
}

/* 初始化锁 */
void lock_init(struct lock *plock) {
    plock->holder = NULL;
    plock->holder_repeat_nr = 0;
    sema_init(&plock->semaphore, 1); // 信号量初始为1
}

/* 信号量down操作 */
void sema_down(struct semaphore *psema) {
    /* 关中断保证原子操作 */
    enum intr_status old_status = intr_disable();
    while(psema->value == 0) {
        // value为0, 表示已被人持有
        ASSERT(!elem_find(&psema->waiters, &running_thread()->general_tag));    // 当前线程不应该在waiters队列中
        if (elem_find(&psema->waiters, &running_thread()->general_tag)) {
            PANIC("sema_down: thread blocked has been in waiters_list\n");
        }
        // value为0,当前线程把自己加入该锁的等待队列
        list_append(&psema->waiters, &running_thread()->general_tag);
        thread_block(TASK_BLOCKED);     // 阻塞线程,直到被唤醒
    }
    // 若value为1或被唤醒后, 会执行下列代码, 也就是获得了锁
    psema->value--;
    ASSERT(psema->value == 0);
    // 恢复中断状态
    intr_set_status(old_status);
}

/* 信号量up操作 */
void sema_up(struct semaphore *psema) {
    /* 关中断保证原子操作 */
    enum intr_status old_status = intr_disable();
    ASSERT(psema->value == 0);
    if (!list_empty(&psema->waiters)) {
        struct task_struct *thread_blocked = elem2entry(struct task_struct, general_tag, list_pop(&psema->waiters));
        thread_unblock(thread_blocked);
    }
    psema->value++;
    ASSERT(psema->value == 1);
    // 恢复中断状态
    intr_set_status(old_status);
}

/* 获取锁 */
void lock_acquire(struct lock *plock) {
    // 排除自己已经持有锁但还未释放的情况
    if (plock->holder != running_thread()) {
        sema_down(&plock->semaphore);   // 对信号量down操作,原子操作
        plock->holder = running_thread();
        ASSERT(plock->holder_repeat_nr == 0);
        plock->holder_repeat_nr = 1;
    } else {
        plock->holder_repeat_nr++;  // 重复获取
    }
}

/* 释放锁 */
void lock_release(struct lock *plock) {
    ASSERT(plock->holder == running_thread());  // 释放锁只有持有者才能释放
    if (plock->holder_repeat_nr > 1) {
        plock->holder_repeat_nr--;
        return;
    }
    ASSERT(plock->holder_repeat_nr == 1);

    plock->holder = NULL;               // 此操作在up之前
    plock->holder_repeat_nr = 0;
    sema_up(&plock->semaphore);         // 对信号量up操作,原子操作
}